/*
 * Author:   W.SH
 */
package com.wgg.crawler.core;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;

import com.wgg.crawler.model.CrawlerDatum;
import com.wgg.crawler.util.HttpMeta;
import com.wgg.util.StringUtils;

/**
 * 〈一句话功能简述〉<br> 
 * 〈功能详细描述〉
 *
 *  @author W.SH
 */
public class HttpSpiderRequest implements SpiderRequest {
        private final static Logger LOG = Logger.getLogger(HttpSpiderRequest.class.getName());
        
        private final static int TIME_OUT = 5000;
        
        private HttpClientGenerator httpClientGenerator;
        
        {
            httpClientGenerator = new HttpClientGenerator();
        }
        
        private CloseableHttpClient generateRequest(CrawlerDatum.Site site) {
            LOG.log(Level.INFO, "generatorClient start....");
            if (isSiteBlank(site)) {
                LOG.log(Level.INFO, "site is not conformance to requirements");
                return httpClientGenerator.generateClient(site);

            }
            return httpClientGenerator.generateClient(site);
        }
        
        //httpClient 最大链接
        public HttpSpiderRequest setPoolSize(int poolSize) {
            httpClientGenerator.setMaxTotal(poolSize);
            return this;
        }
        
        private boolean isSiteBlank(CrawlerDatum.Site site) {
            return (null == site || StringUtils.isBlank(site.getUrl()));
        }

        @Override
        public SpiderResponse getResponse(CrawlerDatum datum) throws Exception {
            if (null == datum || isSiteBlank(datum.getSite())) {
                return null;
            }
            CrawlerDatum.Site site = datum.getSite();
            CloseableHttpClient httpClient = generateRequest(site); //将site的userAgent和 cookies 置入获取的httpClient实例中
            HttpUriRequest httpUriRequest = getHttpUriRequest(datum, new HashMap<String, String>());
            
            HttpResponse response = httpClient.execute(httpUriRequest);
            return handlerResponse(datum, response);
        }
        
        @SuppressWarnings("deprecation")
        private HttpUriRequest getHttpUriRequest(CrawlerDatum datum, Map<String, String> headers) {
            RequestBuilder builder = selectRequestMethod(datum).setUri(datum.getSite().getUrl());
            if (null == headers || headers.isEmpty()) {
                datum.getSite().setHeaders(headers);
            }
            RequestConfig.Builder requestConfigBuilder = RequestConfig.custom().
                    setConnectionRequestTimeout(TIME_OUT).
                    setConnectTimeout(TIME_OUT).
                    setSocketTimeout(TIME_OUT).
                    setCookieSpec(CookieSpecs.BEST_MATCH);
            builder.setConfig(requestConfigBuilder.build());
            return builder.build();
        }
        
        //根据要请求CrawlerDatum中的配置的 mehtod，去执构httpClient execute(我们常用的有 HttpGet HttpPost)对象
        private RequestBuilder selectRequestMethod(CrawlerDatum datum) {
            String method = datum.getMethod();
            if (method == null || method.equalsIgnoreCase(HttpMeta.Method.GET)) { //默认是get
                return RequestBuilder.get();
            } else if(method.equalsIgnoreCase(HttpMeta.Method.POST)) {
                RequestBuilder builder = RequestBuilder.post();
                if (null != datum.getSite().getWireSign() && !datum.getSite().getWireSign().isEmpty()) {
                    NameValuePair[] nameValuePairs = (NameValuePair[]) datum.getSite().getWireSign().get("nameValuePair");
                    if (nameValuePairs.length > 0) {
                        builder.addParameters(nameValuePairs);
                    }
                }
                return builder;
            } else if(method.equalsIgnoreCase(HttpMeta.Method.POST)) {
                RequestBuilder builder = RequestBuilder.post();
                if (null != datum.getSite().getWireSign() && !datum.getSite().getWireSign().isEmpty()) {
                    NameValuePair[] nameValuePairs = (NameValuePair[]) datum.getSite().getWireSign().get("nameValuePair");
                    if (nameValuePairs.length > 0) {
                        builder.addParameters(nameValuePairs);
                    }
                }
                return builder;
            } else if(method.equalsIgnoreCase(HttpMeta.Method.HEAD)) {
                return RequestBuilder.head();
            } else if(method.equalsIgnoreCase(HttpMeta.Method.DELETE)) {
                return RequestBuilder.delete();
            } else if(method.equalsIgnoreCase(HttpMeta.Method.TRACE)) {
                return RequestBuilder.trace();
            }
            throw new IllegalArgumentException("illegal format method: \t" + method);
        }
        
        /**
         * 处理返回的response结果
         */
        private SpiderResponse handlerResponse(CrawlerDatum datum, HttpResponse response) {  
            HttpSpiderResponse html = new HttpSpiderResponse();
            if (null == response) {
                return html;
            }
            try {
                html.setUrl(datum.getSite().getUrl());
                html.setContent(IOUtils.toByteArray(response.getEntity().getContent()));
                html.setHeaders(response.getAllHeaders());
                html.setProtocolVersion(response.getProtocolVersion());
                html.setStatusLine(response.getStatusLine());
            } catch (IOException e) {
                html.setContent("".getBytes());
                LOG.log(Level.WARNING, "response to html getContent error" + e + "\n url: " + datum.getSite().getUrl());
            } finally {
                try {
                    EntityUtils.consume(response.getEntity()); //关闭所有资源
                } catch (IOException e) {
                    LOG.log(Level.WARNING, "op=handlerResponse Exception: " + e + "\n url: " + datum.getSite().getUrl());
                }
            }
            return html;
        }
}
